package com.mgq.netty.bytebuff;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

@Slf4j
public class SelectorNoneBlock {
    public static void main(String[] args) throws IOException {
        //1.创建selector,管理多个channel
        Selector selector = Selector.open();
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);
        // selector管理ServerSocketChannel
        SelectionKey sscKey = ssc.register(selector, 0, null);
        log.debug("register key {}",sscKey);
        //表示只关注accept事件
        sscKey.interestOps(SelectionKey.OP_ACCEPT);
        ssc.bind(new InetSocketAddress(8080));
        while (true) {
            //3.select方法,阻塞方法,没有事件阻塞,有事件线程才会恢复
            selector.select();
            //4.处理事件,selectedKey内部包含了所有发生的事件
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            log.debug("集合大小,{}",selector.selectedKeys().size());
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                //这个key就是sscKey,也就是说把channel注册到selector,selector通过key来管理多个channel.
                //注意,selector处理完事件后,不会将selectedKeys中的key主动移除,需要我们手动来移除.
                iterator.remove();
                log.debug("key:{}",key);
                if (key.isAcceptable()) {
                    ServerSocketChannel channel = (ServerSocketChannel) key.channel();
                    SocketChannel sc = channel.accept();
                    sc.configureBlocking(false);
                    //将socketChannel注册到selector上
                    //将buffer作为key绑定到
                    ByteBuffer buffer = ByteBuffer.allocate(16);
                    SelectionKey scKey=sc.register(selector, 0, buffer);
                    //只关注读事件
                    scKey.interestOps(SelectionKey.OP_READ);
                    log.debug("{}",sc);
                    log.debug("scKey:{}", scKey);
                } else if (key.isReadable()) {
                    try {
                        SocketChannel channel = (SocketChannel) key.channel();
                        //如果是正常断开,read返回值是-1,如果异常断开(比如直接关闭客户端),会抛出一行
                        //客户端断开会触发read事件(不管异常或者正常时间)
                        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();
                        int read = channel.read(byteBuffer);
                        //正常关闭(也就是调用socketChannel.close()方法),不会产生异常.此时key还是没有取消
                        if (read == -1) {
                            key.channel();
                        } else {
                            //切换为读模式
                            split(byteBuffer);
                            //说明没有读取,需要将内容进行复制
                            if (byteBuffer.position() == byteBuffer.limit()) {
                                // 扩容,变为旧buffer的2倍
                                ByteBuffer newByteBuffer = ByteBuffer.allocate(byteBuffer.capacity() * 2);
                                //切换为读模式,准备将旧byteBuffer中的内容复制到新的buffer中
                                byteBuffer.flip();
                                newByteBuffer.put(byteBuffer);
                                //将新的byteBuffer作为新的附件注册
                                key.attach(newByteBuffer);
                            }
                        }

                    } catch (IOException e) {
                        e.printStackTrace();
                        //异常断开,取消key(注意key的remove是从selectedKeys移除);
                        //key.cancel()是从sscKey中取消注册.
                        key.cancel();
                    }
                }
            }
        }
    }
    private static void split(ByteBuffer source) {
        //切换为读模式
        source.flip();
        System.out.println(source.limit());
        for (int i = 0; i < source.limit(); i++) {
            if (source.get(i) == '\n') {
                //第一次position=0,第二次position=\n后面的位置
                int length = i + 1 - source.position();
                //分配的长度应该是读取的长度
                ByteBuffer target = ByteBuffer.allocate(length);
                for (int j = 0; j < length; j++) {
                    target.put(source.get());
                }
                ByteBufferUtil.debugAll(target);
            }
        }

        //切换为写模式,不能使用clear(),可能会有数据没有读完
        source.compact();
    }
}
